#pragma once

#include <cstdio>
#include <ctime>
#include <cstdarg>
#include <cassert>
#include <cassert>
#include <cstring>
#include <cerrno>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <mutex>

#define DEBUG 0
#define NOTICE 1
#define WARNING 2
#define FATAL 3

const char *log_level[] = {"DEBUG", "NOTICE", "WARINING", "FATAL"};

#define LOGFILE "serverTcp.log"

// 下面的Log类和logMessage是两种处理日志的方式

// 管理日志文件, 将日志信息重定向到日志文件中
// 设置为单例模式
class Log
{
private:
    Log() : logFd(-1) {}
    Log(const Log&) = delete;
    void operator=(const Log&) = delete;

    static Log *instance;
    static std::mutex mtx;
public:
    static Log *GetInstance()
    {
        if(nullptr == instance)
        {
            mtx.lock();
            if(nullptr == instance)
            {
                instance = new Log();
            }
            mtx.unlock();
        }

        return instance;
    }

    void enable()
    {
        // 修改文件掩码为0, 防止系统默认掩码影响打开文件的权限
        umask(0);
        // 打开文件
        logFd = open(LOGFILE, O_WRONLY | O_CREAT | O_APPEND, 0666);
        // 如果logFd为-1, 说明打开文件错误
        assert(logFd != -1);
        // 将标准输出和标准错误的内容重定向到日志文件中
        dup2(logFd, 1);
        dup2(logFd, 2);
    }

    ~Log()
    {
        if (logFd != -1)
        {
            // 将日志文件中所有修改过的数据刷新到磁盘上
            fsync(logFd);
            // 关闭日志文件
            close(logFd);
        }
    }
private:
    int logFd;
};
Log* Log::instance = nullptr;
std::mutex Log::mtx;

// 将日志信息直接输出到标准输出或标准错误中
void logMessage(int level, const char *format, ...)
{
    assert(level >= DEBUG);
    assert(level <= FATAL);

    char *name = getenv("USER");

    char logInfo[1024];
    // 定义ap变量, 用于存储可变参数列表
    va_list ap;
    // 初始化ap变量, 使其指向可变参数列表中的第一个参数
    va_start(ap, format);
    // 格式化日志信息, 使用给定的格式字符串和可变参数列表中的参数来生成格式化后的字符串, 并将其存储在logInfo中
    vsnprintf(logInfo, sizeof(logInfo) - 1, format, ap);
    // 清理ap变量, 避免内存泄漏等问题
    va_end(ap);

    // 每次打开太麻烦
    // umask(0);
    // int fd = open(LOGFILE, O_WRONLY | O_CREAT | O_APPEND, 0666);
    // assert(fd >= 0);

    // 如果日志级别为FATAL, 使用标准错误; 否则, 使用标准输出
    FILE *out = (level == FATAL) ? stderr : stdout;
    fprintf(out, "%s | %u | %s | %s\n",
            log_level[level],
            (unsigned int)time(nullptr),
            name == nullptr ? "unknow" : name,
            logInfo);

    fflush(out);        // 将C缓冲区中的数据刷新到OS中
    fsync(fileno(out)); // 将OS中的数据刷新到磁盘中

    // close(fd);
    // char *s = format;
    // while(s){
    //     case '%':
    //         if(*(s+1) == 'd')  int x = va_arg(ap, int);
    //     break;
    // }
}